/*

The MIT License

Copyright (c) 2009 Valentin valyard Simonov ( v@lent.in, http://va.lent.in ).

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

*/

package ru.valyard.smart9slice
{
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.DisplayObject;
	import flash.display.DisplayObjectContainer;
	import flash.display.Sprite;
	import flash.geom.Point;
	import flash.geom.Rectangle;

	/**
	 * Smart9Slice
	 * @author Valentin valyard Simonov ( v@lent.in, http://va.lent.in );
	 * @version 1.1
	 * 
	 * This class allows creating 9-slice scaling sprites without distorted parts. 
	 * Unlike standard flash 9-slice scaling where 5 of 9 object areas are distorted while scaled, 
	 * Smart9Slice uses bitmap fill instead of distortion. 
	 * 
	 * There are 2 ways of using the class:
	 * 1. A sprite in Flash IDE with specially named children
	 * 2. A sprite in Flash IDE with 9-slice scaling enabled
	 * 
	 * <b>Flash IDE with named children</b>
	 * 
	 * Create a MovieClip in Flash IDE, set its class in linkage type to ru.valyard.smart9slice.Smart9Slice.
	 * Add 9 MovieClips as children with following names:
	 * * TL — top left object
	 * * T — top object
	 * * TR — top right object
	 * * L — left object
	 * * C — center object
	 * * R — right object
	 * * BL — bottom left object
	 * * B — bottom object
	 * * BR — bottom right object
	 * These objects will be used while resizing. 
	 * 
	 * To set elements to be distorted either than duplicated when scaled add "_" to element's name, for example TL_ or C_.
	 * 
	 * With such setup Smart9Slice object will be created and ready to use automatically. 
	 *  
	 * <b>Flash IDE with 9-slice scaling</b>
	 * 
	 * Create a MovieClip in Flash IDE, set its class in linkage type to ru.valyard.smart9slice.Smart9Slice.
	 * Add 9-slice scaling grid to it and set it as you need. MovieClip's graphics and scaling grid will be used
	 * for construction of a Smart9Slice object.
	 * 
	 * <b>Advanced</b>
	 * 
	 * Be aware that this object and its children can't be animated (they can, but the snapshot is taken during creation, 
	 * what was visible at that time will be visible throughout the life of this object) and can't be interactive or have
	 * mouse events on them.
	 * 
	 * Width and height are taken from this object width and height on stage during creation, but sometimes (because of masks for example) we need
	 * to specify width and height manually. It's possible if you have an invisible DisplayObject on stage called vMask or
	 * visibilityMask.
	 * 
	 * The same applies to individual MovieClips (Tl, T, TR...), if they have a child named vMask or visibilityMask their
	 * width and height are taken from it.
	 * 
	 */
	public class Smart9Slice extends Abstract9Slice
	{
			
		public function Smart9Slice()
		{
			super();
			
			var w: int;
			var h: int;
			if ( this.getChildByName( "visibilityMask" ) )
			{
				w = this.getChildByName( "visibilityMask" ).width;
				h = this.getChildByName( "visibilityMask" ).height;
			}else if ( this.getChildByName( "vMask" ) )
			{
				w = this.getChildByName( "vMask" ).width;
				h = this.getChildByName( "vMask" ).height;
			}else
			{
				w = this.getOriginalWidth();
				h = this.getOriginalHeight();
			}
			
			w = Math.ceil( w );
			h = Math.ceil( h );
			
			var initObj: Object;
			var rect: Rectangle = this.getOriginalScale9Grid();
			if ( rect )
			{
				rect.x = Math.floor( rect.x );
				rect.y = Math.floor( rect.y );
				rect.width = Math.ceil( rect.width );
				rect.height = Math.ceil( rect.height );
				initObj = createFromGrid( w, h, rect );
			}else if ( hasAppropriateNamedChildren() ){
				var arr: Array = createFromChildren( w, h );
				initObj = arr[0];
				rect = arr[1];
			}else{
				rect = new Rectangle( Math.floor( w/3 ), Math.floor( h/3 ), Math.ceil( w/3 ), Math.ceil( h/3 ) );
				initObj = createFromGrid( w, h, rect );
			}
			
			init( initObj, w, h, rect );
			
			redraw();
			redrawConsts();
			
		}
		
		/**
		 * @inheritDoc
		 */
		protected override function fill( container: Sprite, data: DisplayObject, scale: Boolean, w: int, h: int ): void
		{
			if ( !( data is Bitmap ) )
				return;
				
			var bmpd: BitmapData = ( data as Bitmap ).bitmapData;

			clear( container );
			if ( scale )
			{
				container.addChild( data );
				container.width = w;
				container.height = h;
			}else{
				container.graphics.beginBitmapFill( bmpd );
				container.graphics.drawRect(0, 0, w, h);
				container.graphics.endFill();
			}
		}
		
		
		/**
		 * Initializes the object from scale9Grid rectangle and width+height
		 */
		private function createFromGrid( w: int, h: int, rect: Rectangle ): Object
		{
			
			if ( w == 0 || h == 0 )
				return {};
			
			var fc: int = 0x00000000;
			var zp: Point = new Point( 0, 0 );
			var bmpdSource: BitmapData = new BitmapData( w, h, true, fc );
			var bmpd: BitmapData;
			var obj: Object = {};
			
			bmpdSource.draw( this, null, null, null, new Rectangle(0, 0, w, h) );
			
			// TL
			if ( rect.x > 0 && rect.y > 0 )
			{
				bmpd = new BitmapData( rect.x, rect.y, true, fc );
				bmpd.copyPixels( bmpdSource, new Rectangle( 0, 0, rect.x, rect.y ), zp );
				obj["tl"] = new Bitmap( bmpd, "auto", true );
			}
			// T
			if ( rect.y > 0 && rect.width > 0 )
			{
				bmpd = new BitmapData( rect.width, rect.y, true, fc );
				bmpd.copyPixels( bmpdSource, new Rectangle( rect.x, 0, rect.width, rect.y ), zp );
				obj["t"] = new Bitmap( bmpd, "auto", true );
			}
			// TR
			if ( rect.y > 0 && w-rect.x-rect.width > 0 )
			{
				bmpd = new BitmapData( w-rect.x-rect.width, rect.y, true, fc );
				bmpd.copyPixels( bmpdSource, new Rectangle( rect.x+rect.width, 0, w-rect.x-rect.width, rect.y ), zp );
				obj["tr"] = new Bitmap( bmpd, "auto", true );
			}
			
			// L
			if ( rect.x > 0 && rect.height > 0 )
			{
				bmpd = new BitmapData( rect.x, rect.height, true, fc );
				bmpd.copyPixels( bmpdSource, new Rectangle( 0, rect.y, rect.x, rect.height ), zp );
				obj["l"] = new Bitmap( bmpd, "auto", true );
			}
			// C
			if ( rect.width > 0 && rect.height > 0 )
			{
				bmpd = new BitmapData( rect.width, rect.height, true, fc );
				bmpd.copyPixels( bmpdSource, new Rectangle( rect.x, rect.y, rect.width, rect.height ), zp );
				obj["c"] = new Bitmap( bmpd, "auto", true );
			}
			// R
			if ( rect.height > 0 && w-rect.x-rect.width > 0 )
			{
				bmpd = new BitmapData( w-rect.x-rect.width, rect.height, true, fc );
				bmpd.copyPixels( bmpdSource, new Rectangle( rect.x+rect.width, rect.y, w-rect.x-rect.width, rect.height ), zp );
				obj["r"] = new Bitmap( bmpd, "auto", true );
			}
			
			// BL
			if ( rect.x > 0 && h-rect.y-rect.height > 0 )
			{
				bmpd = new BitmapData( rect.x, h-rect.y-rect.height, true, fc );
				bmpd.copyPixels( bmpdSource, new Rectangle( 0, rect.y+rect.height, rect.x, h-rect.y-rect.height ), zp );
				obj["bl"] = new Bitmap( bmpd, "auto", true );
			}
			// B
			if ( rect.width > 0 && h-rect.y-rect.height > 0 )
			{
				bmpd = new BitmapData( rect.width, h-rect.y-rect.height, true, fc );
				bmpd.copyPixels( bmpdSource, new Rectangle( rect.x, rect.y+rect.height, rect.width, h-rect.y-rect.height ), zp );
				obj["b"] = new Bitmap( bmpd, "auto", true );
			}
			// BR
			if ( w-rect.x-rect.width > 0 && h-rect.y-rect.height > 0 )
			{
				bmpd = new BitmapData( w-rect.x-rect.width, h-rect.y-rect.height, true, fc );
				bmpd.copyPixels( bmpdSource, new Rectangle( rect.x+rect.width, rect.y+rect.height, w-rect.x-rect.width, h-rect.y-rect.height ), zp );
				obj["br"] = new Bitmap( bmpd, "auto", true );
			}
			
			return obj;
			
		}
		
		/**
		 * Initializes the object from children on stage
		 */
		private function createFromChildren( w: int, h: int ): Array
		{

			var child: DisplayObject;
			var child2: DisplayObject;
			var obj: Object = {};
			var rect: Rectangle = new Rectangle(0, 0, 0, 0);

			var r: int = 0;
			var b: int = 0;

			//TL
			child = child2 = null;
			if ( (child = getChildByName( "tl" )) || (child = getChildByName( "TL" )) )
				obj["tl"] = makeBitmap( child );
			else if ( (child2 = getChildByName( "tl_" )) || (child2 = getChildByName( "TL_" )) )
				obj["tl_"] = makeBitmap( child2 );
			rect.x = Math.max( rect.x, int(getItemWidth(child)), int(getItemWidth(child2)) );
			rect.y = Math.max( rect.y, int(getItemHeight(child)), int(getItemHeight(child2)) );
				
			//T
			child = child2 = null;
			if ( (child = getChildByName( "t" )) || (child = getChildByName( "T" )) )
				obj["t"] = makeBitmap( child );
			else if ( (child2 = getChildByName( "t_" )) || (child2 = getChildByName( "T_" )) )
				obj["t_"] = makeBitmap( child2 );
			rect.y = Math.max( rect.y, int(getItemHeight(child)), int(getItemHeight(child2)) );
				
			//TR
			child = child2 = null;
			if ( (child = getChildByName( "tr" )) || (child = getChildByName( "TR" )) )
				obj["tr"] = makeBitmap( child );
			else if ( (child2 = getChildByName( "tr_" )) || (child2 = getChildByName( "TR_" )) )
				obj["tr_"] = makeBitmap( child2 );
			rect.y = Math.max( rect.y, int(getItemHeight(child)), int(getItemHeight(child2)) );
			r = Math.max( r, int(getItemWidth(child)), int(getItemWidth(child2)) );
				
			//L
			child = child2 = null;
			if ( (child = getChildByName( "l" )) || (child = getChildByName( "L" )) )
				obj["l"] = makeBitmap( child );
			else if ( (child2 = getChildByName( "l_" )) || (child2 = getChildByName( "L_" )) )
				obj["l_"] = makeBitmap( child2 );
			rect.x = Math.max( rect.x, int(getItemWidth(child)), int(getItemWidth(child2)) );
				
			//C
			child = child2 = null;
			if ( (child = getChildByName( "c" )) || (child = getChildByName( "C" )) )
				obj["c"] = makeBitmap( child );
			else if ( (child2 = getChildByName( "c_" )) || (child2 = getChildByName( "C_" )) )
				obj["c_"] = makeBitmap( child2 );
				
			//R
			child = child2 = null;
			if ( (child = getChildByName( "r" )) || (child = getChildByName( "R" )) )
				obj["r"] = makeBitmap( child );
			else if ( (child2 = getChildByName( "r_" )) || (child2 = getChildByName( "R_" )) )
				obj["r_"] = makeBitmap( child2 );
			r = Math.max( r, int(getItemWidth(child)), int(getItemWidth(child2)) );
				
			//BL
			child = child2 = null;
			if ( (child = getChildByName( "bl" )) || (child = getChildByName( "BL" )) )
				obj["bl"] = makeBitmap( child );
			else if ( (child2 = getChildByName( "bl_" )) || (child2 = getChildByName( "BL_" )) )
				obj["bl_"] = makeBitmap( child2 );
			rect.x = Math.max( rect.x, int(getItemWidth(child)), int(getItemWidth(child2)) );
			b = Math.max( b, int(getItemHeight(child)), int(getItemHeight(child2)) );
				
			//B
			child = child2 = null;
			if ( (child = getChildByName( "b" )) || (child = getChildByName( "B" )) )
				obj["b"] = makeBitmap( child );
			else if ( (child2 = getChildByName( "b_" )) || (child2 = getChildByName( "B_" )) )
				obj["b_"] = makeBitmap( child2 );
			b = Math.max( b, int(getItemHeight(child)), int(getItemHeight(child2)) );
			
			//BR
			child = child2 = null;
			if ( (child = getChildByName( "br" )) || (child = getChildByName( "BR" )) )
				obj["br"] = makeBitmap( child );
			else if ( (child2 = getChildByName( "br_" )) || (child2 = getChildByName( "BR_" )) )
				obj["br_"] = makeBitmap( child2 );
			b = Math.max( b, int(getItemHeight(child)), int(getItemHeight(child2)) );
			r = Math.max( r, int(getItemWidth(child)), int(getItemWidth(child2)) );
			
			rect.width = w - rect.x - r;
			rect.height = h - rect.y - b;			
			
			return [obj, rect];
		}
		
		/**
		 * Creates a bitmap of the specified DisplayObject
		 */
		private function makeBitmap( obj: DisplayObject ): Bitmap
		{
			var vMask: DisplayObject;
			var w: int = getItemWidth( obj );
			var h: int = getItemHeight( obj );
			var bmpd: BitmapData = new BitmapData( w, h, true, 0x00000000 );
			bmpd.draw( obj, null, null, null, new Rectangle( 0, 0, w, h ) );
			return new Bitmap( bmpd, "auto", true ); 
		}
		
		
		
	}
}